package org.budo.support.lang.util;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.budo.support.java.collection.wrapper.NiceToStringMapWrapper;
import org.budo.support.slf4j.Slf4j;
import org.slf4j.Logger;

/**
 * @author lmw
 */
@SuppressWarnings({ "rawtypes", "unchecked", "cast" })
public class MapUtil {
    private static final Logger log = Slf4j.getLogger();

    public static <K, V> Map<K, V> put(Map<K, V> map, K key, V value) {
        if (null == map) {
            log.error("#25 key=" + key + ", value=" + value + ", map=" + map);
            return newMap(key, value);
        }

        if (null == key || null == value) {
            log.error("#30 key=" + key + ", value=" + value + ", map=" + map);
            return map;
        }

        map.put(key, value);
        return map;
    }

    /**
     * 将Map的Key转为String
     */
    public static <K, V> Map<String, V> stringKeyMap(Map<K, V> map) {
        if (null == map || map.isEmpty()) {
            return (Map<String, V>) map;
        }

        Map result = new HashMap();
        Set<Entry<K, V>> entrySet = map.entrySet();
        for (Entry<K, V> entry : entrySet) {
            K key = entry.getKey();
            if (null == key) {
                continue;
            }

            result.put(key.toString(), entry.getValue());
        }

        return result;
    }

    /**
     * 以两个数组传入key和value，对应组装成map
     */
    public static <K, V> Map<K, V> inject(K[] keys, V[] values) {
        if (null == keys || null == values) {
            return null;
        }

        if (keys.length != values.length) {
            throw new IllegalArgumentException("keys.length=" + keys.length + ", values.length=" + values.length);
        }

        Map<K, V> map = new HashMap<K, V>();
        for (int i = 0; i < keys.length; i++) {
            map.put(keys[i], values[i]);
        }
        return (Map<K, V>) map;
    }

    /**
     * key value 反转
     */
    public static <K, V> Map<V, K> invert(Map<K, V> map) {
        if (null == map) {
            return null;
        }

        Map<V, K> result = new HashMap<V, K>();

        Set<Entry<K, V>> entrySet = map.entrySet();

        for (Entry<K, V> entry : entrySet) {
            K key = entry.getKey();
            V value = entry.getValue();

            if (null != key && null != value) {
                result.put(value, key);
            }
        }
        return result;
    }

    public static Map<String, String> stringMap(String... keyAndValues) {
        Object[] objectKeyAndValues = keyAndValues;
        return newMap(objectKeyAndValues);
    }

    public static Map<String, Object> stringObjectMap(Object... keyAndValues) {
        return newMap(keyAndValues);
    }

    /**
     * 会新创建一个Map 
     */
    public static <K, V> Map<K, V> append(Map<K, V> map, Object... keyAndValues) {
        Map<K, V> result = new HashMap<K, V>();
        result.putAll(map);

        Map newMap = newMap(keyAndValues);
        result.putAll(newMap);
        return result;
    }

    /**
     * 合并多个Map
     */
    public static Map merge(Map... maps) {
        Map result = new HashMap();
        if (null == maps || maps.length < 1) {
            return new NiceToStringMapWrapper(result);
        }

        for (Map map : maps) {
            if (null == map) {
                continue;
            }

            result.putAll(map);
        }
        return new NiceToStringMapWrapper(result);
    }

    public static Map newMap(Object... keyAndValues) {
        if (null == keyAndValues) {
            return null;
        }

        int len = keyAndValues.length;
        if (len % 2 != 0) {
            throw new RuntimeException("参数个数应该为偶数 , len=" + len);
        }

        Map map = new HashMap();
        for (int i = 0; i < len; i = i + 2) {
            map.put(keyAndValues[i], keyAndValues[i + 1]);
        }

        return map;
    }

    public static Boolean containsKey(Map map, String key) {
        return null != map && map.containsKey(key);
    }

    public static boolean isNullOrEmpty(Map map) {
        return null == map || map.isEmpty();
    }

    public static <K, V> String toString(Map<K, V> map) {
        if (null == map) {
            return null;
        }

        Iterator<Entry<K, V>> iterator = map.entrySet().iterator();
        if (!iterator.hasNext()) {
            return "{}";
        }

        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append('{');
        for (;;) {
            Entry<K, V> entry = iterator.next();

            K key = entry.getKey();
            V value = entry.getValue();
            stringBuilder.append(key == map ? "(this Map)" : key);
            stringBuilder.append('=');

            if (null != value && value.getClass().isArray()) {
                stringBuilder.append(ArrayUtil.toString((Object[]) value));
            } else if (null != value && value instanceof Collection) {
                stringBuilder.append(ArrayUtil.toString(((Collection) value).toArray()));
            } else {
                stringBuilder.append(value == map ? "(this Map)" : value);
            }

            if (!iterator.hasNext()) {
                return stringBuilder.append('}').toString();
            }
            stringBuilder.append(',').append(' ');
        }
    }

    public static <K, V> Map<K, V> select(Map<K, V> map, K... keys) {
        int len = keys.length;
        Map<K, V> result = new HashMap<K, V>();
        for (int i = 0; null != keys && i < len; i++) {
            result.put(keys[i], map.get(keys[i]));
        }
        return result;
    }

    /**
     * 移除Map里的指定的key
     */
    public static <K, V> Map<K, V> remove(Map<K, V> map, K... keys) {
        if (null == map || null == keys) {
            return map;
        }

        for (K key : keys) {
            map.remove(key);
        }

        return map;
    }

    public static <K, V> V firstEntryValue(Map<K, V> map) {
        if (null == map || map.isEmpty()) {
            return null;
        }

        Entry<K, V> next = map.entrySet().iterator().next();
        return next.getValue();
    }

    /**
     * 返回以input.value为key，以input.index为value的map
     */
    public static <K> Map<K, Integer> valueToIndexMap(K[] keys) {
        if (null == keys) {
            return null;
        }

        int len = keys.length;
        Map<K, Integer> map = new HashMap<K, Integer>(len);
        for (Integer i = 0; i < len; i++) {
            map.put(keys[i], i);
        }

        return map;
    }

    /**
     * 传入一些Key和共同的Value返回Map
     */
    public static <K, V> Map<K, V> sameValueMap(List<K> keys, V value) {
        if (null == keys) {
            return null;
        }

        int len = keys.size();
        Map<K, V> map = new HashMap<K, V>(len);
        for (Integer i = 0; i < len; i++) {
            map.put(keys.get(i), value);
        }

        return map;
    }

    /**
     * 返回以input.index为key，以input.value为value的map
     */
    public static <K> Map<Integer, K> indexToValueMap(K[] input) {
        if (null == input) {
            return null;
        }

        int len = input.length;
        Map<Integer, K> map = new HashMap<Integer, K>(len);
        for (Integer i = 0; i < len; i++) {
            map.put(i, input[i]);
        }

        return map;
    }

    /**
     * 返回以input.index为key，以input.value为value的map
     */
    public static <K> Map<Integer, K> indexToValueMap(List<K> input) {
        if (null == input) {
            return null;
        }

        return (Map<Integer, K>) indexToValueMap(input.toArray());
    }

    /**
     * 返回以input.value为key，以input.index为value的map
     */
    public static <K> Map<K, Integer> valueToIndexMap(List<K> input) {
        if (null == input) {
            return null;
        }

        return (Map<K, Integer>) valueToIndexMap(input.toArray());
    }

    public static Integer getInteger(Map<String, Object> map, String key, Integer defaultValue) {
        if (null == map || null == key) {
            return null;
        }

        Object value = map.get(key);
        return NumberUtil.toInteger(value, defaultValue);
    }

    public static Integer getInteger(Map<String, Object> map, String key) {
        return getInteger(map, key, null);
    }

    /**
     * 将后面的map中的数据归并到第一个Map，会跳过key或value为空的部分
     */
    public static <K, V> Map<K, V> putAll(Map<K, V> original, Map<?, ?>... append) {
        if (null == original || null == append || append.length < 1) {
            return original;
        }

        for (Map<?, ?> map : append) {
            if (null == map) {
                continue;
            }

            Set<?> entrySet = map.entrySet();
            for (Object each : entrySet) {
                Entry<?, ?> entry = (Entry<?, ?>) each;
                Object key = entry.getKey();
                Object value = entry.getValue();

                if (null == key || null == value) {
                    continue;
                }

                K k = (K) key;
                V v = (V) value;
                original.put(k, v);
            }
        }

        return original;
    }

    /**
     * 返回 key value 交替配对的数组
     */
    public static <T> T[] toPairs(Map<T, T> map) {
        if (null == map) {
            return null;
        }

        List<T> pairs = new ArrayList<T>();
        Set<Entry<T, T>> entrySet = map.entrySet();

        for (Entry<T, T> entry : entrySet) {
            pairs.add(entry.getKey());
            pairs.add(entry.getValue());
        }

        return (T[]) pairs.toArray();
    }

    public static <K, V> V get(Map<K, V> map, K key, V defaultValue) {
        if (null == map || map.isEmpty() || !map.containsKey(key)) {
            return defaultValue;
        }

        return map.get(key);
    }

    public static <K, V> V getFirst(Map<K, V[]> map, K key, V defaultValue) {
        if (null == map || map.isEmpty() || !map.containsKey(key)) {
            return defaultValue;
        }

        return ArrayUtil.first(map.get(key));
    }

    public static <K, V> K getKeyByValue(Map<K, V> map, V value) {
        if (null == map || map.isEmpty() || null == value) {
            return null;
        }

        Set<Entry<K, V>> entrySet = map.entrySet();
        for (Entry<K, V> entry : entrySet) {
            V v = entry.getValue();
            if (null != v && v.equals(value)) {
                return entry.getKey();
            }
        }

        return null;
    }

    public static Object getKeyByValue_1(Object map, Object value) {
        if (null == map || null == value) {
            return null;
        }

        if (!(map instanceof Map)) {
            return null;
        }

        Map m = (Map) map;

        Set entrySet = m.entrySet();
        for (Object each : entrySet) {
            Entry entry = (Entry) each;
            Object v = entry.getValue();
            if (null != v && v.equals(value)) {
                return entry.getKey();
            }
        }

        return null;
    }

    /**
     * 将传入的Map的Value转换为int类型
     */
    public static Map<String, Integer> stringIntegerMap(Map<?, ?> map) {
        if (null == map) {
            return null;
        }

        Map<String, Integer> result = new HashMap<String, Integer>();
        Set<?> entrySet = map.entrySet();
        for (Object each : entrySet) {
            Entry entry = (Entry) each;

            Object key = entry.getKey();
            Object value = entry.getValue();

            String key1 = StringUtil.toString(key);
            Integer value1 = NumberUtil.toInteger(value);

            result.put(key1, value1);
        }

        return result;
    }
}